/*
 * Copyright 2009 Aleksandar Seovic
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.seovic.validation.config;


import java.lang.reflect.Constructor;

import java.util.Properties;

import com.seovic.util.Extractor;
import com.seovic.util.Condition;


/**
 * Provides access to user-configurable defaults needed by the data validation
 * framework.
 *
 * @author Aleksandar Seovic  2009.06.18
 */
@SuppressWarnings("unchecked")
public class Defaults {
    // ---- constants -------------------------------------------------------

    private static final String PROPERTIES_FILE   = "data-validation.properties";
    private static final String DEFAULT_EXTRACTOR = "default.extractor";
    private static final String DEFAULT_CONDITION = "default.condition";

    // ---- singleton instance ----------------------------------------------

    private static final Defaults instance = new Defaults();

    // ---- data members ----------------------------------------------------

    private Constructor extractorCtor;
    private Constructor conditionCtor;


    // ---- constructors ----------------------------------------------------

    /**
     * Construct singleton instance.
     */
    private Defaults() {
        try {
            Properties defaults = new Properties();
            defaults.load(getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE));
            extractorCtor = getConstructor(defaults.getProperty(DEFAULT_EXTRACTOR), String.class);
            conditionCtor = getConstructor(defaults.getProperty(DEFAULT_CONDITION), String.class);
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    // ---- public methods --------------------------------------------------

    /**
     * Return an instance of a default extractor type.
     *
     * @param expression  extractor expression
     *
     * @return default extractor instance
     */
    public static Extractor getDefaultExtractor(String expression) {
        return (Extractor) newInstance(instance.extractorCtor, expression);
    }

    /**
     * Return an instance of a default condition type.
     *
     * @param expression  condition expression
     *
     * @return default Condition instance
     */
    public static Condition getDefaultCondition(String expression) {
        return (Condition) newInstance(instance.conditionCtor, expression);
    }

    // ---- helper methods --------------------------------------------------

    /**
     * Return constructor for a specified class name and argument types.
     *
     * @param className  the name of the class to find a constructor for
     * @param argTypes   constructor argument types
     *
     * @return constructor for a specified class
     *
     * @throws ClassNotFoundException  if class cannot be found
     * @throws NoSuchMethodException   if there is no constructor with the
     *                                 specified argument types
     */
    private static Constructor getConstructor(String className, Class<?>... argTypes)
            throws ClassNotFoundException, NoSuchMethodException {
        Class cls = Class.forName(className);
        return cls.getConstructor(argTypes);
    }

    /**
     * Create a new instance of an object.
     *
     * @param ctor  constructor to invoke
     * @param args  constructor arguments
     *
     * @return created object instance
     */
    private static Object newInstance(Constructor ctor, Object... args) {
        try {
            return ctor.newInstance(args);
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
